//	Copyright (c) 2008, x-port.net ltd.
//
//	All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
//	    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//	    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//	    * Neither the name of the x-port.net ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

/* 
 *	Purpose: Definition of CMultiDispatch
 *	Notes: Implements macros and templates which allow pure automation
 *         clients access to all dual interfaces offered by a given object.
 *         Limitations:
 *              ~ You must derive from IDispatchImpl for your duals.
 *              ~ You cannot overload method names in the union of your duals.
 *                Method names must be unique across all interfaces.
 *              ~ Your DISPIDs must fit within the size of a 16 bit word.
 *          For a full discussion, see http://www.codeproject.com/atl/multidisp.asp
 */


#ifndef ___FP__MULTIDISP_H___
#define ___FP__MULTIDISP_H___

#define INTERFACE_MASK 0xFFFF0000UL
#define DISPID_MASK    0x0000FFFFUL

template <class T>
#pragma warning(disable:4189)
class ATL_NO_VTABLE CMultiDispatch
{
public:
   STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames,
                            UINT cNames, LCID lcid, DISPID* rgdispid)
	{
      setDispatchMapCount();
      
      T* pT = static_cast<T*> (this);
      _TIH_ENTRY* pEntry = pT->GetTypeInfoHolder();
      while (pEntry->ptih != NULL)
      {
         HRESULT hr = pEntry->ptih->GetIDsOfNames(riid, rgszNames,
                                                  cNames, lcid, rgdispid);
         if (SUCCEEDED(hr))
         {
            //
            // Return the DISPID with the encoding OR'd in.
            //
            rgdispid[0] |= pEntry->dispEncode;
            return hr;
         }
         pEntry++;
      }
      return DISP_E_UNKNOWNNAME;
	}

   STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
                     LCID lcid, WORD wFlags, DISPPARAMS* pdispparams,
                     VARIANT* pvarResult, EXCEPINFO* pexcepinfo,
                     UINT* puArgErr)
	{
    T* pT = static_cast<T*> (this);
      _TIH_ENTRY* pEntry = pT->GetTypeInfoHolder();
	  HRESULT hr = DISP_E_MEMBERNOTFOUND;
      while (pEntry->ptih != NULL)
      {
         //
         // Did we set the bit in GetIDsOfNames?  If so, we can invoke
         // with the correct IDispatch.
         //
         if (pEntry->dispEncode == (dispidMember & INTERFACE_MASK))
         {
            //
            // Offset from User class to IDispatchImpl is calculated and stored
            // in the static array.  Using this offset allows us to disambiguate
            // the cast to the IDispatch base pointer.
            //
#pragma warning(disable:4311 4312)
            hr = pEntry->ptih->Invoke((IDispatch*)(((DWORD)pT)+pEntry->offset),
                                              (dispidMember & DISPID_MASK), riid, lcid,
                                              wFlags, pdispparams, pvarResult,
                                              pexcepinfo, puArgErr);
#pragma warning(default:4311 4312)
            if (SUCCEEDED(hr))
               return hr;
         }
         pEntry++;
      }
      return hr;
	}


protected:
   //
   // Encode each entry in the array.  Use the HIWORD for this.
   //
   void setDispatchMapCount()
   {
      static bool counted = false;
      if (!counted)
      {
         counted = true;
         DWORD count = 1;
   
    T* pT = static_cast<T*> (this);
      _TIH_ENTRY* pEntry = pT->GetTypeInfoHolder();
         while (pEntry->ptih != NULL)
         {
            pEntry->dispEncode = count << 16;
            count++;
            pEntry++;
         }
      }
   }
};
#pragma warning(default:4189)

#define DECLARE_MULTI_DISPATCH(T) \
   STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames, \
                            LCID lcid, DISPID* rgdispid) \
	{ \
      return CMultiDispatch<T>::GetIDsOfNames(riid, rgszNames, cNames, \
                                              lcid, rgdispid); \
	} \
   STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, \
                     LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, \
                     EXCEPINFO* pexcepinfo, UINT* puArgErr) \
	{ \
      return CMultiDispatch<T>::Invoke(dispidMember, riid, lcid, wFlags, \
                                       pdispparams, pvarResult, \
                                       pexcepinfo, puArgErr); \
	}

struct _TIH_ENTRY {
   CComTypeInfoHolder *ptih;  // Delegate IDispatch calls to this
   DWORD dispEncode;          // Encoding bit(s)
   DWORD offset;              // offset from derived to IDispatchImpl
};

#define BEGIN_MULTI_DISPATCH_MAP(CLS) \
   typedef CLS theDerived; \
   static struct _TIH_ENTRY* GetTypeInfoHolder() { \
      static struct _TIH_ENTRY pDispEntries[] = {

#define MULTI_DISPATCH_ENTRY(theBase) \
      { &theBase::_tih, 0UL, offsetofclass(theBase, theDerived) },

#define END_MULTI_DISPATCH_MAP() \
   { NULL, 0UL, 0UL } }; \
   return(pDispEntries); }

#endif
